package com.dsunsoft.rtu.service.impl;

import com.dsunsoft.rtu.common.constant.CommonConstants;
import com.dsunsoft.rtu.common.constant.RtuCommandConstants;
import com.dsunsoft.rtu.common.enums.DataTypeEnum;
import com.dsunsoft.rtu.common.util.CommonUtils;
import com.dsunsoft.rtu.common.util.ConvertUtils;
import com.dsunsoft.rtu.dao.DataMapper;
import com.dsunsoft.rtu.dao.RtuDeviceMapper;
import com.dsunsoft.rtu.dao.RtuDeviceRealMapper;
import com.dsunsoft.rtu.dao.RtuStateMapper;
import com.dsunsoft.rtu.entity.RtuDeviceReal;
import com.dsunsoft.rtu.entity.RtuState;
import com.dsunsoft.rtu.entity.StPptnR;
import com.dsunsoft.rtu.entity.StRiverR;
import com.dsunsoft.rtu.netty.NettyChannelMap;
import com.dsunsoft.rtu.properties.AppProperties;
import com.dsunsoft.rtu.query.DataQuery;
import com.dsunsoft.rtu.service.DataService;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import io.netty.channel.Channel;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.MessageFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

@Slf4j
@Service
public class DataServiceImpl implements DataService {

    @Autowired
    private AppProperties properties;
    @Autowired
    private DataMapper mapper;
    @Autowired
    private RtuStateMapper rtuStateMapper;
    @Autowired
    private RtuDeviceMapper rtuDeviceMapper;
    @Autowired
    private RtuDeviceRealMapper rtuDeviceRealMapper;

    /**
     * 保存数据
     * 报头    设备ID  数据长度  设备类型
     * 97 96  00 01   00 0C   07
     * 降雨   报文:  97 96 00 01 00 0C 07 03 08 00 11 00 25 00 38 00 80 (00 或 01) 摄像机状态
     * 水位   报文:  97 96 00 0D 00 09 17 03 06 01 02 03 04 05 06
     *
     * @param hexData 报文
     * @throws Exception e
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void save(String hexData) throws Exception {
        try {
            // 接收时间,去掉秒和毫秒
            Calendar calendar = Calendar.getInstance();
            calendar.set(Calendar.SECOND, 0);
            calendar.set(Calendar.MILLISECOND, 0);
            // 设备ID
            Integer deviceId = Integer.parseInt(hexData.substring(4, 8), 16);
            String deviceIdStr = String.valueOf(deviceId);
            // 设备类型
            String deviceType = hexData.substring(12, 14);
            if (CommonConstants.DEVICE_TYPE_RAINFALL.equals(deviceType)) {
                // 保存降雨
                savePptn(deviceId, calendar.getTime(), hexData);
            } else if (CommonConstants.DEVICE_TYPE_WATER_LEVEL.equals(deviceType)) {
                // 保存水位
                saveRiver(deviceIdStr, calendar.getTime(), hexData);
            } else {
                log.error("未知的设备类型: {}, 报文: {}", deviceType, hexData);
            }

        } catch (Exception e) {
            log.error("保存数据出错: {},报文: {}", e, hexData);
            throw e;
        }
    }

    @Override
    public PageInfo<StPptnR> queryPageByPptn(DataQuery query) {
        Page<StPptnR> page = PageHelper.startPage(query.getPageIndex(), query.getPageSize());
        if (StringUtils.isNotBlank(query.getOrderBy())) {
            page.setUnsafeOrderBy(query.getOrderBy());
        }
        List<StPptnR> list = mapper.selectListByPptn(query);
        return new PageInfo<>(list);
    }

    @Override
    public PageInfo<StRiverR> queryPageByRiver(DataQuery query) {
        Page<StRiverR> page = PageHelper.startPage(query.getPageIndex(), query.getPageSize());
        if (StringUtils.isNotBlank(query.getOrderBy())) {
            page.setUnsafeOrderBy(query.getOrderBy());
        }
        List<StRiverR> list = mapper.selectListByRiver(query);
        return new PageInfo<>(list);
    }

    /**
     * 保存降雨
     *
     * @param deviceId 设备ID
     * @param date     接收日期
     * @param hexData  报文
     */
    private void savePptn(Integer deviceId, Date date, String hexData) {
        // 间隔时间降雨
        BigDecimal drp = ConvertUtils.getDrpValue(hexData.substring(18, 22), properties.getRainGaugeRatio());
        // 1小时降雨量
        BigDecimal drp1 = ConvertUtils.getDrpValue(hexData.substring(22, 26), properties.getRainGaugeRatio());
        // 24小时降雨量
        BigDecimal drp24 = ConvertUtils.getDrpValue(hexData.substring(26, 30), properties.getRainGaugeRatio());
        // 电压
        Double voltage = ConvertUtils.getVoltageValue(hexData.substring(30, 34));
        // 供电状态 1表示485打开，2表示摄像头打开，3表示都打开, 2023-11-13 设备还未修改好，先加代码所以加判断
        int powerSupplyStatus = 0;
        if (hexData.length() >= 36) {
            powerSupplyStatus = Integer.parseInt(hexData.substring(34, 36), 16);
        }
        log.info("报文解析: {}, 设备ID: {},时段降雨: {},1小时降雨: {},24小时降雨: {},电压: {},供电状态: {}",
                hexData, deviceId, drp, drp1, drp24, voltage, powerSupplyStatus);
        // 保存降雨数据
        StPptnR stPptnR = new StPptnR();
        stPptnR.setStcd(String.valueOf(deviceId));
        stPptnR.setTm(date);
        stPptnR.setDrp(drp);
        stPptnR.setDyp(drp24);
        mapper.insertStPptnR(stPptnR);
        // 保存rtu状态
        RtuState rtuState = new RtuState();
        rtuState.setVoltage(voltage);
        rtuState.setId(CommonUtils.getUuid());
        rtuState.setDeviceId(stPptnR.getStcd());
        rtuState.setPowerSupplyStatus(powerSupplyStatus);
        rtuState.setCreatedDate(date);
        rtuStateMapper.insert(rtuState);
        // 检查摄像头电压, 低电压时关闭
        checkCameraVoltage(deviceId, powerSupplyStatus, voltage);
        // 更新最后上报时间
        String extValue = String.format("电压: %s, 供电状态: %s", voltage, powerSupplyStatus);
        updateDeviceRealInfo(stPptnR.getStcd(), DataTypeEnum.PPTN, drp, date, extValue);
    }

    /**
     * 保存水位
     * 站号	 功能码	返回数据长度	数据	        校验码	含义
     * 01	  03	 04	        00000384	FAF0	水位高度900厘米
     * 例：当水位高度为 9米 时的应答数据
     * 01 03 04 00 00 03 84 FA F0
     *
     * @param deviceId 设备ID
     * @param date     接收日期
     * @param hexData  报文
     */
    private void saveRiver(String deviceId, Date date, String hexData) {
        // 水位   报文:  97 96 00 0D 00 09 01 03 04 00 00 03 84 FA F0
        // 查询雷达水位计空高
        BigDecimal radarRange = rtuDeviceMapper.selectRadarRangeById(deviceId);
        // 获取厘米水位, 然后里面转米
        int cmZ = Integer.parseInt(hexData.substring(24, 32), 16);
        BigDecimal z = BigDecimal.valueOf(cmZ).divide(BigDecimal.valueOf(100), 3, RoundingMode.HALF_UP);
        if (radarRange != null) {
            z = z.subtract(radarRange).setScale(3, RoundingMode.HALF_UP);
        }
        log.info("报文解析: {}, 设备ID: {},水位: {}", hexData, deviceId, z);
        StRiverR stRiverR = new StRiverR();
        stRiverR.setStcd(deviceId);
        stRiverR.setTm(date);
        stRiverR.setZ(z);
        stRiverR.setWptn(getWptn(deviceId, z));
        mapper.insertStRiverR(stRiverR);
        // 更新最后上报时间
        updateDeviceRealInfo(deviceId, DataTypeEnum.RIVER, z, date, null);
    }

    /**
     * 更新设备实时信息
     *
     * @param deviceId       设备ID
     * @param dataTypeEnum   数据额类型枚举
     * @param dataVal        数据值
     * @param lastReportDate 上报时间
     * @param extValue       扩展信息
     */
    private void updateDeviceRealInfo(String deviceId, DataTypeEnum dataTypeEnum, BigDecimal dataVal, Date lastReportDate, String extValue) {
        try {
            RtuDeviceReal deviceReal = new RtuDeviceReal();
            deviceReal.setDeviceId(deviceId);
            deviceReal.setDataValue(dataVal);
            deviceReal.setExtValue(extValue);
            deviceReal.setDataType(dataTypeEnum.getValue());
            deviceReal.setLastReportDate(lastReportDate);
            int count = rtuDeviceRealMapper.selectCount(deviceId, dataTypeEnum.getValue());
            if (count > 0) {
                rtuDeviceRealMapper.update(deviceReal);
            } else {
                rtuDeviceRealMapper.insert(deviceReal);
            }
        } catch (Exception e) {
            log.error("更新设备实时信息出错: ", e);
        }
    }

    /**
     * 检查供电电压，小于 10.8V 时关闭, 大于 12.6 开启
     *
     * @param deviceId          设备ID
     * @param powerSupplyStatus 供电状态
     * @param voltage           电压
     */
    private void checkCameraVoltage(Integer deviceId, int powerSupplyStatus, Double voltage) {
        try {
            if (voltage == null) {
                return;
            }
            String command = null; // 指令
            // 16进制设备ID
            String hexDeviceId = CommonUtils.intToHexString(deviceId);
            // 供电状态 1表示485打开，2表示摄像头打开，3表示都打开
            if (voltage <= 10.8 && powerSupplyStatus >= 2) {
                // 关闭摄像机指令
                command = MessageFormat.format(RtuCommandConstants.CLOSE_CAMERA, hexDeviceId);
            }
            if (StringUtils.isNotBlank(command)) {
                Channel socketChannel = NettyChannelMap.get(deviceId);
                if (socketChannel != null && socketChannel.isActive()) {
                    byte[] resMsg = CommonUtils.hexToBytes(command);
                    socketChannel.writeAndFlush(resMsg);
                    log.info("低电压关闭摄像机成功,设备ID: {}", deviceId);
                } else {
                    log.error("低电压关闭摄像机失败,设备ID: {}已断开连接", deviceId);
                }
            }
        } catch (Exception e) {
            log.error("检查摄像机电压出错: ", e);
        }
    }

    /**
     * 获取水势
     *
     * @param stcd 测站ID
     * @param z    水位
     * @return 水势
     */
    private String getWptn(String stcd, BigDecimal z) {
        String wptn = null;
        BigDecimal lastZ = mapper.getLastRiverR(stcd);
        if (lastZ == null) {
            return wptn;
        }
        int compare = z.compareTo(lastZ);
        if (compare == 0) {
            wptn = CommonConstants.WPTN_PING;
        } else if (compare == 1) {
            wptn = CommonConstants.WPTN_ZHANG;
        } else {
            wptn = CommonConstants.WPTN_LUO;
        }
        return wptn;
    }
}
